/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ill_be_bach.view;

import ill_be_bach.model.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import javax.swing.JPanel;

/**
 *
 * @author Abhi
 */
public class SheetMusicRenderer {

    private final int NOTE_WIDTH = 11 ;
    private final int NOTE_HEIGHT = 10 ;
    private final int EIGHTH_LINE_WIDTH = 20;
    private final int EIGHTH_BEATS_PER_LINE = 32 ;
    private final int GAP_BETWEEN_CLEFS = 30 ;
    private final int KEY_SIG_ACCIDENTAL_WIDTH = 6;

    private BufferedImage trebleImage ;
    private BufferedImage bassImage ;
    private ArrayList<LinkedHashMap<Point, Chord>> chordPositionCache = new ArrayList<LinkedHashMap<Point, Chord>>();

    public void draw(Graphics g, JPanel panel, Song song) {
        Graphics2D g2d = (Graphics2D)g;

        trebleImage = ImageUtilities.getBufferedImage(SheetMusicRenderer.class.getResource("resources/treble.jpg").getFile().replaceAll("%20", " "), panel) ;
        bassImage = ImageUtilities.getBufferedImage(SheetMusicRenderer.class.getResource("resources/bass.jpg").getFile().replaceAll("%20", " "), panel) ;

        chordPositionCache.clear();
        for (int i = 0 ; i < 8 ; i++) {
            chordPositionCache.add(new LinkedHashMap<Point, Chord>());
        }

        panel.setBackground(Color.WHITE);

        g2d.setFont(new Font("Courier", Font.BOLD, 20));
        g2d.drawString(song.getTitle(), 200, 15);
        g2d.setFont(new Font("Courier", Font.PLAIN, 14));
        g2d.drawString(song.getTempo() != null? song.getTempo().toString() : "", 0, 35);

        for (int i = 0 ; i < Song.getMaxVoices() ; i++) {
            draw(g2d, panel, song, i);
        }
    }

    public void setSongChordPosition(Song song, int voice, Point clickPosition) {
        // find the chord with the position closest to where the mouse was clicked
        double smallestDist = Integer.MAX_VALUE;
        Point closestChordPosition = null ;
        for (Point chordPosition : this.chordPositionCache.get(voice).keySet()) {
           double distX = Math.abs(chordPosition.getX() - clickPosition.getX());
           double distY = Math.abs(chordPosition.getY() - clickPosition.getY());
           double dist = Math.sqrt(distY*distY + distX*distX);
           if (dist < smallestDist) {
               smallestDist = dist ;
               closestChordPosition = chordPosition ;
           }
        }
        Chord closestChord = this.chordPositionCache.get(voice).get(closestChordPosition);

        if (closestChordPosition != null) {
            song.setCurrentChord(voice, closestChord);
        }
    }

    private int drawLineSignature(Graphics2D g2d, JPanel panel, int y, boolean treble,
            KeySignature keySignature) {
        int xPos = 0 ;
        
        // drawing treble or bass
        BufferedImage image = treble? trebleImage : bassImage ;
        g2d.drawImage(image, null, 0, y) ;
        xPos += image.getWidth() ;

        // drawing key signature
        if (!treble) y+= NOTE_HEIGHT;
        if (keySignature == KeySignature.CMajor_AMinor) {
        }
        else if (keySignature == KeySignature.GMajor_EMinor) {
            xPos += drawSharp(g2d, xPos, y - NOTE_HEIGHT/2);
        }
        else if (keySignature == KeySignature.DMajor_BMinor) {
            xPos += drawSharp(g2d, xPos, y - NOTE_HEIGHT/2);
            xPos += drawSharp(g2d, xPos, y + NOTE_HEIGHT);
        }
        else if (keySignature == KeySignature.AMajor_FSharpMinor) {
            xPos += drawSharp(g2d, xPos, y - NOTE_HEIGHT/2);
            xPos += drawSharp(g2d, xPos, y + NOTE_HEIGHT);
            xPos += drawSharp(g2d, xPos, y - NOTE_HEIGHT);
        }
        else if (keySignature == KeySignature.EMajor_CSharpMinor) {
            xPos += drawSharp(g2d, xPos, y - NOTE_HEIGHT/2);
            xPos += drawSharp(g2d, xPos, y + NOTE_HEIGHT);
            xPos += drawSharp(g2d, xPos, y - NOTE_HEIGHT);
            xPos += drawSharp(g2d, xPos, y + NOTE_HEIGHT/2);
        }
        else if (keySignature == KeySignature.BMajor_GSharpMinor) {
            xPos += drawSharp(g2d, xPos, y - NOTE_HEIGHT/2);
            xPos += drawSharp(g2d, xPos, y + NOTE_HEIGHT);
            xPos += drawSharp(g2d, xPos, y - NOTE_HEIGHT);
            xPos += drawSharp(g2d, xPos, y + NOTE_HEIGHT/2);
            xPos += drawSharp(g2d, xPos, y + 2*NOTE_HEIGHT);
        }
        else if (keySignature == KeySignature.FSharpMajor_DSharpMinor) {
            xPos += drawSharp(g2d, xPos, y - NOTE_HEIGHT/2);
            xPos += drawSharp(g2d, xPos, y + NOTE_HEIGHT);
            xPos += drawSharp(g2d, xPos, y - NOTE_HEIGHT);
            xPos += drawSharp(g2d, xPos, y + NOTE_HEIGHT/2);
            xPos += drawSharp(g2d, xPos, y + 2*NOTE_HEIGHT);
            xPos += drawSharp(g2d, xPos, y);
        }
        else if (keySignature == KeySignature.GFlatMajor_EFlatMinor) {
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT*3/2);
            xPos += drawFlat(g2d, xPos, y);
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT*2);
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT/2);
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT*5/2);
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT);
        }
        else if (keySignature == KeySignature.DFlatMajor_BFlatMinor) {
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT*3/2);
            xPos += drawFlat(g2d, xPos, y);
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT*2);
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT/2);
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT*5/2);
        }
        else if (keySignature == KeySignature.AFlatMajor_FMinor) {
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT*3/2);
            xPos += drawFlat(g2d, xPos, y);
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT*2);
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT/2);
        }
        else if (keySignature == KeySignature.EFlatMajor_CMinor) {
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT*3/2);
            xPos += drawFlat(g2d, xPos, y);
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT*2);
        }
        else if (keySignature == KeySignature.BFlatMajor_GMinor) {
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT*3/2);
            xPos += drawFlat(g2d, xPos, y);
        }
        else if (keySignature == KeySignature.FMajor_DMinor) {
            xPos += drawFlat(g2d, xPos, y + NOTE_HEIGHT*3/2);
        }
        xPos += KEY_SIG_ACCIDENTAL_WIDTH ;

        return xPos ;

    }

    private void drawLines(Graphics2D g2d, int x, int y, int lineLength) {
        int lineYPosition = y ;
        for (int i = 0 ; i < 5 ; i++) {
            g2d.drawLine(x, lineYPosition, x + lineLength, lineYPosition);
            lineYPosition += NOTE_HEIGHT ;
        }
    }

    private int drawSharp(Graphics2D g2d, int x, int y) {
        g2d.setFont(new Font("Courier", Font.PLAIN, 22));
        g2d.drawString("♯", x, y + NOTE_HEIGHT + 4);
        return KEY_SIG_ACCIDENTAL_WIDTH ;
    }

    private int drawFlat(Graphics2D g2d, int x, int y) {
        g2d.setFont(new Font("Courier", Font.PLAIN, 22));
        g2d.drawString("♭", x, y + NOTE_HEIGHT);
        return KEY_SIG_ACCIDENTAL_WIDTH ;
    }

    private int drawNatural(Graphics2D g2d, int x, int y) {
        g2d.setFont(new Font("Courier", Font.PLAIN, 22));
        g2d.drawString("♮", x + 5, y + NOTE_HEIGHT + 4);
        return KEY_SIG_ACCIDENTAL_WIDTH ;
    }

    private void draw(Graphics2D g2d, JPanel panel, Song song, int voice) {
        int curXPosition = 0 ;
        int curYPosition = 45;
        Dimension curPanelSize = new Dimension(0,0);

        boolean treble = song.getTrebleVoices().contains(voice);

        if (!treble)
            curYPosition += NOTE_HEIGHT * 4 + GAP_BETWEEN_CLEFS ;

        int numBeatsInLine = 0 ;

        for (Chord chord : song.getChords(voice))
        {
            // if chord will push beat over limit of beats per line, move to new line
            if (numBeatsInLine + chord.beat.getEighthBeatsCount()  > EIGHTH_BEATS_PER_LINE) {
                numBeatsInLine = 0 ;
                curXPosition = 0 ;
                curYPosition += NOTE_HEIGHT * 4 * 2 + GAP_BETWEEN_CLEFS + 40 ;
            }
            // draw line signature if starting at new line
            if (numBeatsInLine == 0) {
                curXPosition += drawLineSignature(g2d, panel, curYPosition, treble, song.getKeySignature());
                drawLines(g2d, 0, curYPosition, curXPosition);
            }
            numBeatsInLine += chord.beat.getEighthBeatsCount();

            // draw current chord position
            if (chord.equals(song.getCurrentChord(voice))) {
                g2d.setColor(Color.red);
                g2d.drawLine(curXPosition, curYPosition, curXPosition, curYPosition + NOTE_HEIGHT * 5);
                g2d.setColor(Color.black);
            }

            // hash current chord position
            chordPositionCache.get(voice).put(new Point(curXPosition, curYPosition), chord);

            int smallestYPosition = Integer.MAX_VALUE ;
            for (Note note : chord.notes) {
                int noteYPosition = curYPosition ;
                noteYPosition += -5 + NOTE_HEIGHT * (treble? 5 : 6); // move to base C Position

                if (note.key != Key.Rest) {
                    if (note.key == Key.C)
                        noteYPosition -= 0 ;
                    else if(note.key == Key.D)
                        noteYPosition -= (int)(.5 * NOTE_HEIGHT) ;
                    else if (note.key == Key.E)
                        noteYPosition -= (int)(NOTE_HEIGHT) ;
                    else if (note.key == Key.F)
                        noteYPosition -= (int)(1.5 * NOTE_HEIGHT) ;
                    else if (note.key == Key.G)
                        noteYPosition -= (int)(2 * NOTE_HEIGHT) ;
                    else if (note.key == Key.A)
                        noteYPosition -=  (int)(2.5 * NOTE_HEIGHT) ;
                    else if (note.key == Key.B)
                        noteYPosition -= (int)(3 * NOTE_HEIGHT) ;
                    noteYPosition -= 7*(NOTE_HEIGHT/2) * (note.octave - (treble? 5 : 3)) ;
                    if (noteYPosition < smallestYPosition)
                        smallestYPosition = noteYPosition ;

                    // draw note
                    if (chord.beat == Beat.HALF || chord.beat == Beat.WHOLE)
                        g2d.drawOval(curXPosition, noteYPosition, NOTE_WIDTH, NOTE_HEIGHT) ;
                    else
                        g2d.fillOval(curXPosition, noteYPosition, NOTE_WIDTH, NOTE_HEIGHT);

                    // draw line through note if outside main lines
                    if ((noteYPosition > 4*NOTE_HEIGHT && (noteYPosition - 4*NOTE_HEIGHT)%NOTE_HEIGHT == 0)
                            || (noteYPosition < curYPosition && (curYPosition - noteYPosition - NOTE_HEIGHT/2)%NOTE_HEIGHT == 0))
                        g2d.drawLine(curXPosition - 2, noteYPosition + NOTE_HEIGHT/2, curXPosition + NOTE_WIDTH + 2, noteYPosition + NOTE_HEIGHT/2);

                    // draw accidentals
                    if (note.accidental == Accidental.Sharp) {
                        drawSharp(g2d, curXPosition + NOTE_WIDTH/2, noteYPosition);
                    }
                    else if(note.accidental == Accidental.Flat) {
                        drawFlat(g2d, curXPosition + NOTE_WIDTH/2, noteYPosition);
                    }
                    else if (note.accidental == Accidental.Natural) {
                        drawNatural(g2d, curXPosition + NOTE_WIDTH/2, noteYPosition) ;
                    }
                }
                else { // key is rest
                    if (chord.beat == Beat.EIGHTH) {
                        BufferedImage image = ImageUtilities.getBufferedImage(SheetMusicRenderer.class.getResource("resources/eighthRest.jpg").getFile().replaceAll("%20", " "), panel) ;
                        g2d.drawImage(image, null, curXPosition, (int)(curYPosition + 1.5*NOTE_HEIGHT)) ;
                    }
                    else if(chord.beat == Beat.QUARTER) {
                        BufferedImage image = ImageUtilities.getBufferedImage(SheetMusicRenderer.class.getResource("resources/quarterRest.jpg").getFile().replaceAll("%20", " "), panel) ;
                        g2d.drawImage(image, null, curXPosition, curYPosition + (NOTE_HEIGHT - 2)) ;
                    }
                    else if(chord.beat == Beat.HALF) {
                        g2d.fillRect(curXPosition, curYPosition + 2*NOTE_HEIGHT + 2, 15, 3);
                    }
                    else if(chord.beat == Beat.WHOLE) {
                        g2d.fillRect(curXPosition, curYPosition + (int)(curYPosition + 1.5*NOTE_HEIGHT + 1), 15, 3);
                    }
                }
            }

            // draw lines for note
            if (smallestYPosition != Integer.MAX_VALUE) {
                if (chord.beat != Beat.WHOLE)
                    g2d.drawLine(curXPosition + NOTE_WIDTH - 1, smallestYPosition + 4, curXPosition + NOTE_WIDTH - 1, smallestYPosition - 20);
                if (chord.beat == Beat.EIGHTH)
                    g2d.drawArc(curXPosition + NOTE_WIDTH - 9, smallestYPosition - 20, 15, 30, 0, 90) ;
            }

            // draw 5 lines
            int lineLength = 0 ;
            if (chord.beat == Beat.EIGHTH) {
                lineLength += EIGHTH_LINE_WIDTH ;
            }
            else if (chord.beat == Beat.QUARTER) {
                lineLength += EIGHTH_LINE_WIDTH * 2 ;
            }
            else if (chord.beat == Beat.HALF) {
                lineLength += EIGHTH_LINE_WIDTH * 4 ;
            }
            else if (chord.beat == Beat.WHOLE) {
                lineLength += EIGHTH_LINE_WIDTH * 8 ;
            }
            drawLines(g2d, curXPosition, curYPosition, lineLength);
            
            curXPosition += lineLength ;
            panel.setPreferredSize(new Dimension(0, curYPosition + 150));
        }
    }
}
